// ArrayBuffer转16进度字符串示例
const ab2hex = buffer=> {
  const hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function(bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  );
  return hexArr.join('');
};

const array2arrayBuffer = array=>{
  return new Uint8Array(array).buffer;
};

const utf8arrayFixedLength = (array,fixedLength=15)=>{
  const len = array.length;
  // const fixedLength = 15;
  if(len > fixedLength){
    array.splice(fixedLength);
  }else{
    for(let i=len;i<fixedLength;i++){
      array.push(0);
    }
  }
  return array;
};

const string2utf8array = str=>{
  let bytes = [];
  let c;
  const len = str.length;
  for(let i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if(c >= 0x010000 && c <= 0x10FFFF) {
      bytes.push(((c >> 18) & 0x07) | 0xF0);
      bytes.push(((c >> 12) & 0x3F) | 0x80);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if(c >= 0x000800 && c <= 0x00FFFF) {
      bytes.push(((c >> 12) & 0x0F) | 0xE0);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if(c >= 0x000080 && c <= 0x0007FF) {
      bytes.push(((c >> 6) & 0x1F) | 0xC0);
      bytes.push((c & 0x3F) | 0x80);
    } else {
      bytes.push(c & 0xFF);
    }
  }
  return bytes;
};

const utf8array2string = arr=>{
  if(typeof arr === 'string') {
    return arr;
  }
  let str = '',
    _arr = arr;
  for(let i = 0; i < _arr.length; i++) {
    let one = _arr[i].toString(2),
      v = one.match(/^1+?(?=0)/);
    if(v && one.length === 8) {
      let bytesLength = v[0].length;
      let store = _arr[i].toString(2).slice(7 - bytesLength);
      for(let st = 1; st < bytesLength; st++) {
        store += _arr[st + i].toString(2).slice(2);
      }
      str += String.fromCharCode(parseInt(store, 2));
      i += bytesLength - 1;
    } else {
      str += String.fromCharCode(_arr[i]);
    }
  }
  return str;
};

const convert2Str = content=>{
  const type = typeof content;
  if('number'===type||'boolean'===type){
    content += '';
  }else{
    if(Object.prototype.toString.call(content).slice(8,-1).toLowerCase()==='object'){
      content = JSON.stringify(content)
    }else if('string'!==type){
      content = '无法转为string'
    }
  }
  return content;
};

const alert = content=>{
  wx.showModal({content:convert2Str(content)});
};

const log = info=>{
  // console.log(convert2Str(info));
};

const digit2hex = digit=>{
  return ('00' + digit.toString(16)).slice(-2);
};


const getSeqNum = ()=>{
  let seqNum = 0;
  return ()=>{
    return (seqNum++)%256;
  }
};

const isIOS = wx.getSystemInfoSync().model.toLowerCase().indexOf("iphone")!==-1;

const timeFormat = update=>{
  // time 单位 秒

  const now = Math.floor((new Date().getTime())/1000);
  const interval = now - update;

  let desc = '';
  const secondsOfOneDay = 24*60*60;

  if(interval < secondsOfOneDay){
    const updateDate = new Date(update*1000);
    desc = `更新于${('00'+updateDate.getHours()).slice(-2)}点${('00'+updateDate.getMinutes()).slice(-2)}分`;
  }else{
    desc = `更新于${(interval/secondsOfOneDay).toFixed()}天前`;
  }
  return desc;
  
};

const jump = ({pageName,params={},type=0/*redirect:0,navigate:1*/})=>{
  let query = [];
  for(let k in params){
    if(params.hasOwnProperty(k)){
      query.push(k+'='+params[k]);
    }
  }
  query = query.join('&');
  wx[0===type?'redirectTo':'navigateTo']({url: `/pages/${pageName}/${pageName}`+(query?`?${query}`:'')});
};

const obj2query = obj=> {
  if (obj) {
    let ret = [];
    for (let k in obj) {
      if(obj.hasOwnProperty(k)){
        //todo obj[k]若是object，若含有：等敏感字符，需编码
        ret.push(k + '=' + obj[k]);
      }
    }
    return ret.join('&');
  } else {
    return '';
  }
};

const query2obj = (str/*http://xx?a=1&b=2*/)=> {
  let ret = {};
  try {
    str = str.substr(str.indexOf('?') + 1);
    const arr = str.split("&");
    for (let i = 0; i < arr.length; i++) {
      const num = arr[i].indexOf("=");
      if (num > 0) {
        let name = arr[i].substring(0, num);
        ret[name] = arr[i].substr(num + 1);
      }
    }
  } catch (e) {
    ret = {};
  }
  return ret;
};

const objValues = obj=>{
  let d = [];
  for(let k in obj){
    if(obj.hasOwnProperty(k)){
      d.push(obj[k]);
    }
  }
  return d;
};

const getNTPtime = ()=>{
  const time = parseInt((''+(new Date().getTime())/1000),10);
  const str = time.toString(16); //预期：必定是8位的字符串
  const low = parseInt(str.substring(6,8),16);
  const middle1 = parseInt(str.substring(4,6),16);
  const middle2 = parseInt(str.substring(2,4),16);
  const high = parseInt(str.substring(0,2),16);
  return [low,middle1,middle2,high];
};

const json2string = (data='')=>{
  let ret = '';
  try{
    ret = 'string'===typeof data?data:JSON.stringify(data);
  }catch(e){
    ret = '';
  }
  return ret;
};

const toast = (info)=>{
  wx.showToast({
    title:info||'抱歉，出错了',
    icon:'none',
    mask:true
  })
};

const getCubeName = ({serialNumber})=>{
  let name = '检测盒子';
  try{
    if(serialNumber){
      let info = wx.getStorageSync(serialNumber+'');
      info = 'string' === typeof info?JSON.parse(info):info;
      name = info.cubeName||'检测盒子'
    }else{
      name = '检测盒子';
    }
  }catch (e) {
    name = '检测盒子';
  }

  return name;


  // let cube = '';
  // if(serialNumber&&serialNumber.length===20&&name){
  //   cube = [name,serialNumber.substring(16)].join('_');
  // }else{
  //   cube = '';
  // }
  // return cube;
};


module.exports = {
  ab2hex,
  alert,
  array2arrayBuffer,
  digit2hex,
  getSeqNum:getSeqNum(),
  isIOS,
  timeFormat,
  // debug,
  string2utf8array,
  utf8array2string,
  utf8arrayFixedLength,
  log,
  jump,
  obj2query,
  query2obj,
  objValues,
  getNTPtime,
  json2string,
  toast,
  convert2Str,
  getCubeName,
  navigateBack:function () {
    if(getCurrentPages().length>1){
      wx.navigateBack();
    }else{
      jump({pageName:'devices'});
    }
  },
  whenOfflineSaveInfo:function(){
    const {setCubeInfo} = module.exports;
    try{
      const serialNumber = wx.getStorageSync('lastCubeSerialNumber')+'';
      setCubeInfo({serialNumber,info:{matchStatus: 4, isCharging: 0}});
    }catch(e){
      log(`whenOfflineSaveInfo error: ${'string'===typeof e?e:JSON.stringify(e)}`);
    }
  },
  whenOnlineSaveInfo:function(){
    const {setCubeInfo} = module.exports;
    try{
      const serialNumber = wx.getStorageSync('lastCubeSerialNumber')+'';
      setCubeInfo({serialNumber,info:{matchStatus: 3}});
    }catch(e){
      log(`whenOnlineSaveInfo error: ${'string'===typeof e?e:JSON.stringify(e)}`);
    }
  },
  whenConnectedSaveInfo:function({serialNumber,cubeName}){
    const {getPageInstance,matchedDevicesHandler} = module.exports;
    matchedDevicesHandler({serialNumber,cubeName,action:'add'});
    wx.setStorageSync('lastCubeSerialNumber', serialNumber+'');
    //wx.setStorageSync('lastCubeName', cubeName);
    // const deviceInfoInstance = getPageInstance('deviceInfo');
    // if(deviceInfoInstance){
    //   deviceInfoInstance.setData({matchStatus:3});
    // }
  },
  goToDeviceInfo:function({serialNumber,needConnect}){
    // if(needConnect-0 === 1){
    //
    // }

    wx.setStorageSync('lastCubeSerialNumber', serialNumber+'');
    //wx.setStorageSync('lastCubeName', cubeName);

    wx.setStorageSync('needConnect', needConnect);

    const deltaInfo = module.exports.pageDeltaInStack('deviceInfo');
    if(deltaInfo>0){
      wx.navigateBack({delta:deltaInfo});
    }else{
      const deltaList = module.exports.pageDeltaInStack('devices');
      wx[deltaList===0?'navigateTo':'redirectTo']({url:'/pages/deviceInfo/deviceInfo'});
    }
  },
  matchedDevicesHandler:function({serialNumber,cubeName,action}){
    let matchedDevices = wx.getStorageSync('matchedDevices')||{};
    try{
      matchedDevices = 'string'===typeof matchedDevices?JSON.parse(matchedDevices):matchedDevices;
    }catch (e) {
      matchedDevices = {};
    }
    if(action==='add'){
      matchedDevices[serialNumber] = {serialNumber,cubeName};
    }else if(action==='delete'){
      if(matchedDevices[serialNumber]){
        delete matchedDevices[serialNumber];
      }
    }else if(action==='modify'){
      matchedDevices[serialNumber].cubeName = cubeName;
    }
    wx.setStorageSync('matchedDevices',JSON.stringify(matchedDevices));
  },
  getInfoByDeviceId:function (id) {
    let ret = {};
    try {
      const res = wx.getStorageInfoSync();
      const {keys} = res;
      let k,v;
      for(let i=0;i<keys.length;i++){
        k = keys[i];
        v = wx.getStorageSync(k);
        if(v.indexOf(id+'')!==-1){
          v = 'string'===typeof v?JSON.parse(v):v;
          v.serialNumber = k+'';
          ret = v;
          break;
        }
      }
    } catch (e) {
      ret = null;
    }
    return ret;
  },
  currentPageName:function () {
    const pages = getCurrentPages();
    const {length} = pages;
    return pages[length-1].route.split('/')[2];
  },
  pageDeltaInStack:function (pageName) {
    if(!pageName){
      return -1;
    }
    const pages = getCurrentPages();
    const {length} = pages;
    let name;
    if(length>1){
      for(let i=0;i<length;i++){
        name = pages[i].route.split('/')[2];
        if(name.toLowerCase() === pageName.toLowerCase()){
          return length-1-i;
        }
      }
    }else{
      name = pages[0].route.split('/')[2];
      if(name.toLowerCase() === pageName.toLowerCase()){
        return 0;
      }else{
        return -2;
      }
    }

  },
  getCubeInfoJson:function(serialNumber/*可为空*/){
    let info = null;
    try{
      const id = serialNumber?(serialNumber+''):(wx.getStorageSync('lastCubeSerialNumber')+'');
      info = wx.getStorageSync(id);
      info = 'string' === typeof info?JSON.parse(info):info;
    }catch (e) {
      info = null;
    }
    return info;
  },
  cubeIsOnline:function (serialNumber/*可为空*/) {
    const info = module.exports.getCubeInfoJson(serialNumber);
    return info && info.matchStatus-0 === 3;
  },
  findMatchedDevices:function () {

    let matchedDevices = wx.getStorageSync('matchedDevices')||{};
    try{
      matchedDevices = 'string'===typeof matchedDevices?JSON.parse(matchedDevices):matchedDevices;
    }catch (e) {
      matchedDevices = {};
    }

    return module.exports.objValues(matchedDevices);


    // let ret = [];
    // try {
    //   const res = wx.getStorageInfoSync();
    //   const {keys=[]} = res;
    //   for(let i=0;i<keys.length;i++){
    //     const k = keys[i];
    //     //k是纯数字，且是20位
    //     if(/^\d{20}$/.test(k)){
    //       let v = wx.getStorageSync(k);
    //       v = 'string' === typeof v?JSON.parse(v):v;
    //       let temp = Object.assign({},v);
    //       temp.serialNumber = k+'';
    //       ret.push(temp);
    //     }
    //   }
    // } catch (e) {
    //   ret = [];
    // }
    // return ret;
  },
  getPageInstance:function(pageName){
    if(!pageName){
      return null;
    }else{
      const pages = getCurrentPages();
      const {length} = pages;
      let name;
      for(let i=0;i<length;i++){
        name = pages[i].route.split('/')[2];
        if(name.toLowerCase() === pageName.toLowerCase()){
          return pages[i];
        }
      }
      return null;
    }
  },
  serialNumberLength:20,
  setCubeInfo: function ({serialNumber,info={}}) {
    let data = wx.getStorageSync(serialNumber + '') || {};
    data = 'string' === typeof data ? JSON.parse(data) : data;
    const ret = Object.assign({}, data, info);
    wx.setStorageSync(serialNumber + '', JSON.stringify(ret));
  },
  lengthCount:function(str) {
    let joiner = '\u{200D}';
    let split = str.split(joiner);
    let count = 0;
    for (let s of split) {
      count += Array.from(s.split(/[\ufe00-\ufe0f]/).join('')).length;
    }
    return count / split.length;
  },
  throttle:function(func, wait, options={}) {
    var leading = true,
      trailing = true;

    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;

    const {debounce} = module.exports;
    return debounce(func, wait, {
      'leading': leading,
      'maxWait': wait,
      'trailing': trailing
    });
  },
  debounce:function(func, wait, options={}) {
    var lastArgs,
      lastThis,
      maxWait,
      result,
      timerId,
      lastCallTime,
      lastInvokeTime = 0,
      leading = false,
      maxing = false,
      trailing = true;


    wait = wait || 0;
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? Math.max(options.maxWait || 0, wait) : maxWait;
    trailing = 'trailing' in options ? !!options.trailing : trailing;


    function invokeFunc(time) {
      var args = lastArgs,
        thisArg = lastThis;

      lastArgs = lastThis = undefined;
      lastInvokeTime = time;
      result = func.apply(thisArg, args);
      return result;
    }

    function leadingEdge(time) {
      // Reset any `maxWait` timer.
      lastInvokeTime = time;
      // Start the timer for the trailing edge.
      timerId = setTimeout(timerExpired, wait);
      // Invoke the leading edge.
      return leading ? invokeFunc(time) : result;
    }

    function remainingWait(time) {
      var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime,
        timeWaiting = wait - timeSinceLastCall;

      return maxing
        ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
        : timeWaiting;
    }

    function shouldInvoke(time) {
      var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime;

      // Either this is the first call, activity has stopped and we're at the
      // trailing edge, the system time has gone backwards and we're treating
      // it as the trailing edge, or we've hit the `maxWait` limit.
      return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
        (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
    }

    function timerExpired() {
      var time = new Date().getTime();
      if (shouldInvoke(time)) {
        return trailingEdge(time);
      }
      // Restart the timer.
      timerId = setTimeout(timerExpired, remainingWait(time));
    }

    function trailingEdge(time) {
      timerId = undefined;

      // Only invoke if we have `lastArgs` which means `func` has been
      // debounced at least once.
      if (trailing && lastArgs) {
        return invokeFunc(time);
      }
      lastArgs = lastThis = undefined;
      return result;
    }

    function cancel() {
      if (timerId !== undefined) {
        clearTimeout(timerId);
      }
      lastInvokeTime = 0;
      lastArgs = lastCallTime = lastThis = timerId = undefined;
    }

    function flush() {
      return timerId === undefined ? result : trailingEdge(now());
    }

    function debounced() {
      var time = new Date().getTime(),
        isInvoking = shouldInvoke(time);

      lastArgs = arguments;
      lastThis = this;
      lastCallTime = time;

      if (isInvoking) {
        if (timerId === undefined) {
          return leadingEdge(lastCallTime);
        }
        if (maxing) {
          // Handle invocations in a tight loop.
          timerId = setTimeout(timerExpired, wait);
          return invokeFunc(lastCallTime);
        }
      }
      if (timerId === undefined) {
        timerId = setTimeout(timerExpired, wait);
      }
      return result;
    }
    debounced.cancel = cancel;
    debounced.flush = flush;
    return debounced;
  }
};
